package com.tianhai.db;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 性能测试类 - 测试存储引擎的性能指标
 */
public class PerformanceTest {
    
    // 测试参数
    private static final int TOTAL_RECORDS = 100000;  // 10万条记录
    private static final int RECORD_SIZE = 100;       // 每条记录约100字节
    private static final double MAX_TIME_PER_OP_MS = 5.0;  // 单操作最大耗时5ms
    private static final double MAX_FILE_SIZE_RATIO = 1.2;  // 文件大小不超过理论值的1.2倍
    
    // 记录结构
    private static class Record {
        int id;
        String data;
        
        Record(int id, String data) {
            this.id = id;
            this.data = data;
        }
        
        byte[] toBytes() {
            String combined = id + "|" + data;
            return combined.getBytes(StandardCharsets.UTF_8);
        }
        
        int getSize() {
            return toBytes().length;
        }
    }
    
    public static void main(String[] args) {
        System.out.println("========================================");
        System.out.println("    存储引擎性能测试");
        System.out.println("========================================\n");
        
        DiskManager diskManager = null;
        String dbFilePath = "performance_test.db";
        
        try {
            // 清理旧的测试文件
            File oldFile = new File(dbFilePath);
            if (oldFile.exists()) {
                oldFile.delete();
                System.out.println("✓ 已清理旧测试文件\n");
            }
            
            // 创建DiskManager
            System.out.println("【初始化】");
            diskManager = new DiskManager(dbFilePath);
            System.out.println("✓ DiskManager创建成功");
            System.out.println("  - 数据库文件: " + dbFilePath);
            System.out.println();
            
            // 性能测试配置
            System.out.println("【测试配置】");
            System.out.println("  - 记录总数: " + formatNumber(TOTAL_RECORDS) + " 条");
            System.out.println("  - 每条记录大小: ~" + RECORD_SIZE + " 字节");
            System.out.println("  - 性能要求: 单操作 < " + MAX_TIME_PER_OP_MS + " ms");
            System.out.println("  - 文件大小要求: < " + MAX_FILE_SIZE_RATIO + " 倍理论值");
            System.out.println();
            
            // 生成测试数据
            System.out.println("【生成测试数据】");
            long dataGenStart = System.currentTimeMillis();
            List<Record> records = generateRecords(TOTAL_RECORDS, RECORD_SIZE);
            long dataGenEnd = System.currentTimeMillis();
            
            // 计算实际平均记录大小
            long totalRecordBytes = 0;
            for (Record record : records) {
                totalRecordBytes += record.getSize();
            }
            double avgRecordSize = (double) totalRecordBytes / TOTAL_RECORDS;
            
            System.out.println("✓ 测试数据生成完成");
            System.out.println("  - 生成耗时: " + (dataGenEnd - dataGenStart) + " ms");
            System.out.println("  - 平均记录大小: " + String.format("%.2f", avgRecordSize) + " 字节");
            System.out.println();
            
            // 执行插入性能测试
            System.out.println("【插入性能测试】");
            System.out.println("正在插入 " + formatNumber(TOTAL_RECORDS) + " 条记录...");
            
            InsertResult insertResult = performInsertTest(diskManager, records);
            
            System.out.println("\n✓ 插入测试完成\n");
            
            // 输出插入性能统计
            System.out.println("【插入性能统计】");
            System.out.println("  - 总耗时: " + insertResult.totalTimeMs + " ms (" + 
                String.format("%.2f", insertResult.totalTimeMs / 1000.0) + " 秒)");
            System.out.println("  - 平均每条记录耗时: " + 
                String.format("%.4f", insertResult.avgTimePerRecordMs) + " ms");
            System.out.println("  - 吞吐量: " + 
                String.format("%.0f", insertResult.throughputPerSec) + " 条/秒");
            System.out.println("  - 总页数: " + insertResult.totalPages);
            System.out.println("  - 总槽位数: " + insertResult.totalSlots);
            
            // 性能要求检查
            boolean perfTestPassed = insertResult.avgTimePerRecordMs < MAX_TIME_PER_OP_MS;
            System.out.println("\n  性能要求检查: " + (perfTestPassed ? "✓ 通过" : "✗ 失败"));
            if (!perfTestPassed) {
                System.out.println("    实际: " + String.format("%.4f", insertResult.avgTimePerRecordMs) + 
                    " ms > 要求: " + MAX_TIME_PER_OP_MS + " ms");
            } else {
                System.out.println("    实际: " + String.format("%.4f", insertResult.avgTimePerRecordMs) + 
                    " ms < 要求: " + MAX_TIME_PER_OP_MS + " ms ✓");
            }
            System.out.println();
            
            // 刷新到磁盘
            System.out.println("【刷新到磁盘】");
            long flushStart = System.currentTimeMillis();
            diskManager.flush();
            long flushEnd = System.currentTimeMillis();
            System.out.println("✓ 数据已刷新到磁盘");
            System.out.println("  - 刷新耗时: " + (flushEnd - flushStart) + " ms");
            System.out.println();
            
            // 关闭DiskManager
            diskManager.close();
            diskManager = null;
            
            // 文件大小分析
            System.out.println("【文件大小分析】");
            File dbFile = new File(dbFilePath);
            long actualFileSize = dbFile.length();
            
            // 计算理论文件大小
            long theoreticalSize = totalRecordBytes;
            long theoreticalSizeWithPages = (long) insertResult.totalPages * Page.PAGE_SIZE;
            
            double fileSizeRatio = (double) actualFileSize / theoreticalSizeWithPages;
            
            System.out.println("  - 实际文件大小: " + formatBytes(actualFileSize));
            System.out.println("  - 理论数据大小: " + formatBytes(theoreticalSize));
            System.out.println("  - 理论页大小: " + formatBytes(theoreticalSizeWithPages) + 
                " (" + insertResult.totalPages + " 页 × " + Page.PAGE_SIZE + " 字节)");
            System.out.println("  - 文件大小比率: " + String.format("%.4f", fileSizeRatio));
            System.out.println("  - 空间利用率: " + 
                String.format("%.2f%%", (double) theoreticalSize * 100 / actualFileSize));
            
            // 文件大小要求检查
            boolean fileSizeTestPassed = fileSizeRatio <= MAX_FILE_SIZE_RATIO;
            System.out.println("\n  文件大小检查: " + (fileSizeTestPassed ? "✓ 通过" : "✗ 失败"));
            if (!fileSizeTestPassed) {
                System.out.println("    实际比率: " + String.format("%.4f", fileSizeRatio) + 
                    " > 要求: " + MAX_FILE_SIZE_RATIO);
            } else {
                System.out.println("    实际比率: " + String.format("%.4f", fileSizeRatio) + 
                    " < 要求: " + MAX_FILE_SIZE_RATIO + " ✓");
            }
            System.out.println();
            
            // 读取性能测试（可选）
            System.out.println("【读取性能测试】");
            diskManager = new DiskManager(dbFilePath);
            
            ReadResult readResult = performReadTest(diskManager, insertResult.pageSlotMap);
            
            System.out.println("✓ 读取测试完成");
            System.out.println("  - 读取记录数: " + readResult.recordsRead);
            System.out.println("  - 总耗时: " + readResult.totalTimeMs + " ms");
            System.out.println("  - 平均每条记录耗时: " + 
                String.format("%.4f", readResult.avgTimePerRecordMs) + " ms");
            System.out.println("  - 数据一致性: " + (readResult.dataValid ? "✓ 通过" : "✗ 失败"));
            System.out.println();
            
            // 最终测试结果
            System.out.println("========================================");
            System.out.println("           测试结果汇总");
            System.out.println("========================================");
            
            boolean allTestsPassed = perfTestPassed && fileSizeTestPassed && readResult.dataValid;
            
            System.out.println("插入性能测试: " + (perfTestPassed ? "✓ 通过" : "✗ 失败"));
            System.out.println("文件大小测试: " + (fileSizeTestPassed ? "✓ 通过" : "✗ 失败"));
            System.out.println("数据一致性测试: " + (readResult.dataValid ? "✓ 通过" : "✗ 失败"));
            System.out.println("----------------------------------------");
            
            if (allTestsPassed) {
                System.out.println("✓✓✓ 所有性能测试通过！");
            } else {
                System.out.println("✗✗✗ 部分性能测试失败");
            }
            
            System.out.println("========================================\n");
            
        } catch (Exception e) {
            System.err.println("✗ 测试过程中发生异常: " + e.getMessage());
            e.printStackTrace();
        } finally {
            if (diskManager != null && !diskManager.isClosed()) {
                try {
                    diskManager.close();
                } catch (IOException e) {
                    System.err.println("✗ 关闭DiskManager时发生异常: " + e.getMessage());
                }
            }
        }
    }
    
    /**
     * 生成测试记录
     */
    private static List<Record> generateRecords(int count, int dataSize) {
        List<Record> records = new ArrayList<>(count);
        Random random = new Random(12345); // 固定种子以便复现
        
        for (int i = 0; i < count; i++) {
            // 生成随机数据
            StringBuilder data = new StringBuilder();
            int remainingSize = dataSize - String.valueOf(i).length() - 1; // 减去ID和分隔符
            
            while (data.length() < remainingSize) {
                // 生成可读的随机字符串
                char c = (char) ('A' + random.nextInt(26));
                data.append(c);
            }
            
            records.add(new Record(i, data.toString()));
            
            // 进度显示
            if ((i + 1) % 10000 == 0) {
                System.out.print("  生成进度: " + formatNumber(i + 1) + " / " + 
                    formatNumber(count) + " (" + (i + 1) * 100 / count + "%)\r");
            }
        }
        
        return records;
    }
    
    /**
     * 执行插入性能测试
     */
    private static InsertResult performInsertTest(DiskManager diskManager, List<Record> records) 
            throws IOException {
        
        long startTime = System.currentTimeMillis();
        
        Page currentPage = null;
        int totalSlots = 0;
        List<PageSlotInfo> pageSlotMap = new ArrayList<>();
        
        for (int i = 0; i < records.size(); i++) {
            Record record = records.get(i);
            byte[] recordBytes = record.toBytes();
            
            // 如果当前页为空或已满，分配新页
            if (currentPage == null || currentPage.isFull()) {
                if (currentPage != null) {
                    diskManager.writePage(currentPage);
                }
                currentPage = diskManager.allocateNewPage();
            }
            
            // 在当前页中分配槽位并写入记录
            int slotIndex = currentPage.allocateSlot();
            currentPage.writeSlot(slotIndex, recordBytes);
            totalSlots++;
            
            // 记录位置信息
            pageSlotMap.add(new PageSlotInfo(currentPage.getPageId(), slotIndex, record.id));
            
            // 进度显示（每1000条显示一次）
            if ((i + 1) % 1000 == 0) {
                double progress = (i + 1) * 100.0 / records.size();
                System.out.print("  进度: " + formatNumber(i + 1) + " / " + 
                    formatNumber(records.size()) + " (" + String.format("%.1f", progress) + "%)\r");
            }
        }
        
        // 写入最后一页
        if (currentPage != null) {
            diskManager.writePage(currentPage);
        }
        
        long endTime = System.currentTimeMillis();
        long totalTimeMs = endTime - startTime;
        
        InsertResult result = new InsertResult();
        result.totalTimeMs = totalTimeMs;
        result.avgTimePerRecordMs = (double) totalTimeMs / records.size();
        result.throughputPerSec = records.size() * 1000.0 / totalTimeMs;
        result.totalPages = diskManager.getTotalPages();
        result.totalSlots = totalSlots;
        result.pageSlotMap = pageSlotMap;
        
        return result;
    }
    
    /**
     * 执行读取性能测试
     */
    private static ReadResult performReadTest(DiskManager diskManager, List<PageSlotInfo> pageSlotMap) 
            throws IOException {
        
        long startTime = System.currentTimeMillis();
        
        int recordsRead = 0;
        boolean dataValid = true;
        
        // 采样读取（每100条读取一次，避免时间过长）
        int sampleInterval = 100;
        
        for (int i = 0; i < pageSlotMap.size(); i += sampleInterval) {
            PageSlotInfo info = pageSlotMap.get(i);
            
            Page page = diskManager.readPage(info.pageId);
            byte[] data = page.readSlot(info.slotIndex);
            
            // 验证数据（检查ID是否正确）
            String dataStr = new String(data, StandardCharsets.UTF_8).trim();
            if (dataStr.startsWith(info.recordId + "|")) {
                recordsRead++;
            } else {
                dataValid = false;
                System.err.println("数据不一致: 期望ID=" + info.recordId + ", 实际数据=" + 
                    dataStr.substring(0, Math.min(20, dataStr.length())));
            }
        }
        
        long endTime = System.currentTimeMillis();
        
        ReadResult result = new ReadResult();
        result.recordsRead = recordsRead;
        result.totalTimeMs = endTime - startTime;
        result.avgTimePerRecordMs = (double) result.totalTimeMs / recordsRead;
        result.dataValid = dataValid;
        
        return result;
    }
    
    /**
     * 格式化数字（添加千位分隔符）
     */
    private static String formatNumber(int number) {
        return String.format("%,d", number);
    }
    
    /**
     * 格式化字节大小
     */
    private static String formatBytes(long bytes) {
        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes < 1024 * 1024) {
            return String.format("%.2f KB", bytes / 1024.0);
        } else if (bytes < 1024 * 1024 * 1024) {
            return String.format("%.2f MB", bytes / (1024.0 * 1024.0));
        } else {
            return String.format("%.2f GB", bytes / (1024.0 * 1024.0 * 1024.0));
        }
    }
    
    /**
     * 插入测试结果
     */
    private static class InsertResult {
        long totalTimeMs;
        double avgTimePerRecordMs;
        double throughputPerSec;
        int totalPages;
        int totalSlots;
        List<PageSlotInfo> pageSlotMap;
    }
    
    /**
     * 读取测试结果
     */
    private static class ReadResult {
        int recordsRead;
        long totalTimeMs;
        double avgTimePerRecordMs;
        boolean dataValid;
    }
    
    /**
     * 页-槽位映射信息
     */
    private static class PageSlotInfo {
        int pageId;
        int slotIndex;
        int recordId;
        
        PageSlotInfo(int pageId, int slotIndex, int recordId) {
            this.pageId = pageId;
            this.slotIndex = slotIndex;
            this.recordId = recordId;
        }
    }
}
